import { generate } from "./generate";

export function compileToFunction(elStr) {
    // /src/compile / generate.js代码
    let ast = parseHTML(elStr)
    // 抽象语法树转字符串，为代码生成做准备
    // _c('div',{id:"app",style:{"background":"green","color":"red","font-size":"20px"}},_v("hello"+_s(msg)+","+_s(age)),_c('h2',null,_v("张三")))
    //_c(tag, attrs, children, text)：这个函数是要创建一个tag的元素，并且它的属性是attrs，儿子是children，文本内容是text
    // _s(变量)：这个函数的作用是将要插值表达式中的变量{{name}}转成字符串
    // _v(text)：这个函数的作用是要创建文本的
    let m = generate(ast)
    // 这里打印就能看到转换的结果
    // 将redner字符串变成函数
    // this指向window，可以调用这个函数打印this，这里不重要，方法调用可以使用call，bind，apply改变this指向，这里的this要指向挂载到vm的data属性,替代render函数中的形参为实参
    let render = new Function(`with(this){return ${m}}`)
    console.log(render);
    return render

}
// 标签名 a-aaa
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`;
// 命名空间标签 aa:aa-xxx
const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
// 开始标签-捕获标签名
const startTagOpen = new RegExp(`^<${qnameCapture}`);
// 结束标签-匹配标签结尾的 </div>
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`);
// 匹配属性
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/;
// 匹配标签结束的 >
const startTagClose = /^\s*(\/?)>/;
// 匹配 {{ }} 表达式
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g;
// 创建ast抽象语法树
function createASTElement(tag, attrs) {
    return {
        tag,
        attrs,
        children: [],
        type: 1,
        parent: null
    }
}
// ---------------------------------------------------------------------------
// 是否有根元素
let root
// 当前元素的父元素
let createParent
// 栈结构 生成父子关系
let stack = []
function start(tag, attrs) {
    let element = createASTElement(tag, attrs)
    if (!root) {
        root = element
    }
    createParent = element
    stack.push(element)
}
function character(text) {
    // 替换掉空格和\n
    text = text.replace(/\s/g, '')
    if (text.trim()) {
        createParent.children.push({
            type: 3,
            text
        })
    }
}

function end(tag) {
    let element = stack.pop()
    createParent = stack[stack.length - 1]
    if (createParent) {
        element.parent = createParent.tag
        createParent.children.push(element)
    }
}
// ---------------------------------------------------------------------------
// 解析html字符串
function parseHTML(html) {
    // 不知道多少层，所以使用while循环
    // 终止条件：当html为空，结束循环
    while (html) {
        debugger
        // 判断html是不是标签开头的
        let textEnd = html.indexOf("<")
        if (textEnd === 0) {
            // 开始标签
            const startTagMatch = parseStartTag()
            if (startTagMatch) {
                start(startTagMatch.tagName, startTagMatch.attrs)
                continue
            }
            // 结束标签
            let endTagMatch = html.match(endTag)
            if (endTagMatch) {
                advance(endTagMatch[0].length)
                end(endTagMatch[1])
                continue
            }
        }
        let text
        // 文本数据
        if (textEnd > 0) {
            // 删除并获取被删除的内容
            text = html.substring(0, textEnd)
        }
        if (text) {
            advance(text.length)
            character(text)
        }
    }
    // 解析开始标签
    function parseStartTag() {
        const start = html.match(startTagOpen)
        if (start) {
            let match = {
                tagName: start[1],
                attrs: []
            }
            // 删除开始标签
            advance(start[0].length)
            // 解析属性
            // 一个标签可以有多个属性，需要遍历
            let attr
            // 结束标签 >
            let end
            // 如果
            while (!(end = html.match(startTagClose)) && (attr =
                html.match(attribute))) {
                match.attrs.push({
                    name: attr[1],
                    value: attr[3] || attr[4] || attr[5]
                })
                advance(attr[0].length)
            }
            // 删除开始标签结束的>
            if (end) {
                advance(end[0].length)
                return match
            }
        }
    }
    // 删除解析过的标签
    function advance(length) {
        html = html.substring(length)
    }
    // ---------------------------------------------------------------------------
    return root
    // ---------------------------------------------------------------------------
}
